var c;
var ctx;
var padding = 20;
var md = 0;

//embedded audio
var iframeElement; 
var iframeElementID;
var widget1;        
var widget2;        
var scplaying = false;

var DoublePI = Math.PI * 2;
var windowIsActive = true;

//Request Animation frame polyfill
//from http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
(function() {
    var lastTime = 0;
    var vendors = ['webkit', 'moz'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame =
          window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());


var mouse = {
	x: 0,
	y: 0,
	lastSeen: 0,
	lastCalcTime: 0,
	distance: 1000,
	
	handleMouseMove: function(event) {
		this.x = event.clientX-7;
		this.y = event.clientY-7;
		this.lastSeen = Date.now();
		this.lastCalcTime = this.lastSeen;
		this.distance = Math.sqrt(Math.pow(this.x-target.x,2) + Math.pow(this.y-target.y,2));
		if (this.distance < target.radius) {
			target.mouseInside();
			//console.log("d=" + this.distance);
		}
	},
	getDistance: function() {
		if (this.lastCalcTime > 500) {
			this.lastCalcTime = Date.now();
			this.distance = Math.sqrt(Math.pow(this.x-target.x,2) + Math.pow(this.y-target.y,2));
		}
		return this.distance;
	},
}

var fpswatcher = {
	lastTicTime: 0,
	maxTics: 20,
	tics: [],
	tic: function() {
		var n = Date.now();
		var t = n - this.lastTicTime;
		this.tics.push(t);
		if (this.tics.length > this.maxTics) this.tics.shift;
		this.lastTicTime = n;
		return t;
	},
	calcITI: function() {
		var sum=0;
		for (var i = 0; i < this.tics.length; i++) {
			sum += this.tics[i];
		}
		return sum/this.tics.length;
	}
}

var target = {
	x: 0,
	y: 0,
	radius: 0,
	nextRadius: 0,
	lastRadius: 0,
	radiusChangeDuration: 1000,
	radiusChangeStartTime: 0,
	maxRadius: 40,
	maxSpeed: .05,
	minSpeed: .01,
	speed: .05,	//pixels per ms
	maxx: 0,
	maxy: 0,
	direction: 0,
	inited: false,
	updated: false,
	createdTime: 0,
	mouseInsideNow: false,
	ticsSinceMouseInside: 0,
	mouseInsideSamples: [],
	maxMouseSamples: 60,
	lastPerformance: 0,
	performanceThreshold: .9,
	lastPerformanceCheckTime: 0,
	performanceCheckInterval: 2000,
	sampleProbability: .05,
	soundSr1: null,

	spiral: {
		imgObj: {
			source: null,
			current: 0,
			totalFrames: 6,
			width: 99,
			height: 99
		},
		img: new Image(),
		minChangeInterval: 100,
		nextChangeTime: 0,
		init: function() {
			this.img.src = "spiralstrip.png";
			this.imgObj.source = this.img;
			console.log("spiral has loaded...");
		},
		draw: function(context, x, y) {
			var iobj = this.imgObj;
			if (iobj != null) {
				context.drawImage(
					iobj.source, 
					iobj.current * iobj.width,
					0,
					iobj.width,
					iobj.height,
					x-iobj.width/2,
					y-iobj.height/2,
					iobj.width,
					iobj.height);
				//console.log("drew spiral " + iobj.current + " " + x + "," + y);
			}
			if (Date.now() >= this.nextChangeTime) {
				iobj.current = (iobj.current + 1) % iobj.totalFrames;
				this.nextChangeTime = Date.now() + this.minChangeInterval;
				//console.log("frame: " + iobj.current);
			}
		}
	},

	mouseInside: function() {
		this.ticsSinceMouseInside = 0;
		this.sampleMouse(this.ticsSinceMouseInside);
	},
	sampleMouse: function(s, force) {
		if (force || Math.random() <= this.sampleProbability) {
			if (s==0) this.mouseInsideSamples.push(1);
			else this.mouseInsideSamples.push(0);

			if (this.mouseInsideSamples.length > this.maxMouseSamples)
				this.mouseInsideSamples.shift();
		}
	},
	getPerformance: function() {
		var sum=0;
		for (var i = 0; i < this.mouseInsideSamples.length; i++) {
			sum+= this.mouseInsideSamples[i];
		}
		this.lastPerformance = sum / this.mouseInsideSamples.length;
		
		if (this.mouseInsideSamples.length == this.maxMouseSamples &&
			this.mouseInsideSamples[this.mouseInsideSamples.length-1] == 1 &&  
			this.lastPerformance >= this.performanceThreshold) {
			this.makeSmaller();
			this.mouseInsideSamples = [1];
		} else if (this.mouseInsideSamples[this.mouseInsideSamples.length-1] == 0 && 
			this.radius < this.maxRadius) {
			this.makeBigger();
		}
		
		if (this.radius > this.maxRadius) this.radius = this.maxRadius;

		this.lastPerformanceCheckTime = Date.now();
		return this.lastPerformance;
	},
	makeSmaller: function() {
		//srEngine.newSr();
		//this.soundSr1.play();
		//console.log("sound!");
		return;
		this.nextRadius = this.radius * .9;
		this.lastRadius = this.radius;
		this.radiusChangeStartTime = Date.now();
	},
	makeBigger: function() {
		this.nextRadius = Math.min(this.radius * 1.1,this.maxRadius);
		
		this.lastRadius = this.radius;
		this.radiusChangeStartTime = Date.now();
	},
	manageRadius: function() {
		if (this.nextRadius == 0) return;

		var deltat = Date.now() - this.radiusChangeStartTime;
		if (deltat < this.radiusChangeDuration) {
			this.radius = this.lastRadius + 
				(this.nextRadius - this.lastRadius) * deltat / this.radiusChangeDuration;
		} else {
			this.radius = this.nextRadius;
			this.nextRadius = 0;
		}
	},
	init: function () {
		this.createdTime = Date.now();
		this.radius = this.maxRadius;
		this.maxx = c.width; //window.innerWidth;
		this.maxy = c.height; //window.innerHeight;
		this.x = Math.random()*(this.maxx-this.radius*2) + this.radius;
		this.y = Math.random()*(this.maxy-this.radius*2) + this.radius;
		this.direction = Math.random() * 6.28;
		this.updated = true;
		this.inited = true;

		this.spiral.init();
		this.soundSr1 = new buzz.sound("sr1.ogg", {preload: true, autoplay: false, loop: false});
		//initialize load gifs associated with the animation
		//this.spiralGif = new Image();
		//this.spiralGif.src = "spiralsmall.gif";
	},
	tic: function(delta) {
		if (this.inited == false) {
			this.init();
			delta = 0;
		} else if (delta > 1000) {
			delta = 0;
			//console.log("setting delta to 0");
		}

		this.sampleMouse(this.ticsSinceMouseInside);
		if (window.innerWidth != c.width) {
			c.width = window.innerWidth-padding;
			this.maxx = c.width;
		}
		if (window.innerHeight != c.height) {
			c.height = window.innerHeight-padding;
			this.maxy = c.height;
		}
		var directionVariance = Math.sin(Date.now()) * 1;
		//console.log(directionVariance);
		this.direction += directionVariance * (Math.random()-.5);
		//if (Date.now() - this.createdTime > 1000) return;
		var s = Math.sin(Date.now()) + Math.random() * .01;
		
		this.manageRadius();

		if (mouse.getDistance() < this.radius+1) this.speed = this.maxSpeed;
		else this.speed = this.minSpeed;
	
		if (this.speed > 0) {
			var distance = delta * (this.speed + .02 * s); 
			var x = this.x + Math.cos(this.direction) * distance;
			var y = this.y + Math.sin(this.direction) * distance;
		
			if (x > this.x && x+this.radius >= this.maxx) {
				if (y < this.y) this.direction -= 1.57;
				else this.direction += 1.57;
			}
			if (y > this.y && y+this.radius >= this.maxy) {
				if (x > this.x) this.direction -= 1.57; 
				else this.direction += 1.57;
			}
			if (x < this.x && x-this.radius <= 0) {
				if (y > this.y) this.direction -= 1.57;
				else this.direction += 1.57;
			}
			if (y < this.y && y-this.radius <= 0) {
				if (x > this.x) this.direction += 1.57;
				else this.direction -= 1.57;
			}
		
			this.x = this.x + Math.cos(this.direction) * distance;
			this.y = this.y + Math.sin(this.direction) * distance;
			//console.log("dist="+distance);
		}

		this.updated = true;
		if (mouse.getDistance() > this.radius+1) {
			this.ticsSinceMouseInside++;
			//this.sampleMouse(1,true);
		} else {
			this.ticsSinceMouseInside = 0;
			srEngine.respond();
			//this.sampleMouse(0,true);
		}
	},
	draw: function() {
		if (!this.updated) return;

		if (1) {
			if (this.mouseInsideSamples[this.mouseInsideSamples.length-1] == 1) {
				ctx.fillStyle="#00FF00";
				scController.play();
			} else {
				ctx.fillStyle = "#DBDBDB"
				scController.pause();
			}
		} else {
			if (mouse.getDistance() <= this.radius+1) {
				ctx.fillStyle="#00FF00";
				scController.play();
			} else {
				ctx.fillStyle = "#DBDBDB"
				scController.pause();
			}
		}
		
		if (mouse.getDistance() <= this.radius + 1) {
			c.style.cursor = "none";
			this.spiral.draw(ctx, this.x, this.y);
			ctx.beginPath();
			ctx.fillStyle="#00FF00";
			ctx.strokeStyle="#FFFFFF";
			ctx.lineWidth = 80;
			ctx.arc(mouse.x, mouse.y, this.radius+ctx.lineWidth/2, 0, DoublePI);
			ctx.stroke();
			messageController.draw(this.x, this.y);
		} else {
			c.style.cursor = "default";
			ctx.beginPath();
			ctx.fillStyle="#DBDBDB";
			ctx.strokeStyle="#000000";
			ctx.lineWidth = 1;
			ctx.arc(this.x, this.y, this.radius+ctx.lineWidth/2, 0, DoublePI);
			//ctx.stroke();
			ctx.fill();
		}

		//messageController.draw(this.x, this.y);
		//ctx.fill();
		//this.spiral.draw(ctx, this.x, this.y);

		//ctx.beginPath();
		//ctx.fillStyle="#00FF00";
		//ctx.strokeStyle="#FFFFFF";
		//ctx.lineWidth = 80;
		//ctx.arc(this.x, this.y, this.radius+ctx.lineWidth/2, 0, DoublePI);
		//ctx.stroke();
		//else console.log(mouse.distance + " " + mouse.getLastDistance());
		this.updated = false;
	}
}

var srEngine = {
	maxAge: 1000,
	maxSrs: 20,
	srs: [],
	speed: .05,
	responses: 0,
	srProbability: .001,
	respond: function() {
		this.responses++;
		if (Math.random() <= this.srProbability) {
			this.newSr();
		}
	},
	newSr: function() {
		target.soundSr1.play();
		var sr = {
			startTime: Date.now(),
			endTime: Date.now() + this.maxAge,
			x: mouse.x,
			y: mouse.y,
			offset: -99,
			maxAge: this.maxAge,
			text: "+1",
			done: false,
			speed: this.speed,
			draw: function(delta) {
				if (this.done) return;
				if (Date.now() > this.endTime) this.done = true;
				var alpha = (1-(Math.max(1,Date.now() - this.startTime))/this.maxAge);

				ctx.font="20px Georgia";
				ctx.fillStyle = "rgba(0,0,0,"+alpha+")";
				
				var distance = this.speed * delta;
				this.y -= distance;
				//console.log("\t" + delta);
				if (this.offset == -99) 
					this.offset = ctx.measureText(this.text).width / 2;

				ctx.fillText("+1",this.x-this.offset,this.y);
			}
		}
		this.srs.push(sr);
		if (this.srs.length > this.maxSrs) this.srs.shift();
	},
	draw: function(delta) {
		if (this.srs.length > this.maxSrs) this.srs.shift();
		for (var i = 0; i < this.srs.length; i++) {
			this.srs[i].draw(delta);
		}
	}
}

var scController = {
	playing: false,
	loop: true,

	positionRequestInterval: 1000,
	lastPositionRequestTime: 0,
	lastPositionRequestReceivedTime: 0,
	nextPositionRequestTime: 0,
	position: 0,

	receivedPosition: function(p) {
		this.lastPositionRequestReceivedTime = Date.now();
		var delta = this.LastPositionRequestReceivedTime - this.lastPositionRequestTime;
		this.position = p.currentPosition;
		var now = Date.now();
		this.nextPositionRequestTime = now + 1000;
	},
	requestPosition: function() {
		//console.log("requesting position");
		this.lastPositionRequestTime = Date.now();
		widget1.getPosition(this.receivedPosition);
	},
	play: function() {
		if (SC.Widget.Events.READY == false) return;
		if (this.playing == false) widget1.play();
		this.playing = true;
	},
	pause: function() {
		if (SC.Widget.Events.READY == false) return;
		if (this.playing == true) widget1.pause();
		this.playing = false;
	},
	loop: function() {
		this.pause();
		this.play();
		console.log("looping");
	}

}

var messageController = {
	messageDuration: 1,
	nextShowTime: 0,
	interMessageInterval: 5000,
	currentMessage: -1,

	messages: [
		["so", "easy", "to", "relax"],
		["no","thoughts"],
		["quiet", "mind"],
		["slow", "down"],
		["no", "inner", "voice"],
		["points", "are", "pleasure"],
		["points", "are", "fun"],
		["must", "earn", "points"],
		["earn", "points"],
		["will", "work", "hard", "for", "points"],
		["earn", "points", "for", "obedience"],
		["obedience", "is", "pleasure"],
		["points", "are", "obedience"],
		["slave", "to", "the", "spiral"],
		["follow", "the", "spiral"],
		["empty", "mind"],
		["empty", "will"],
		["no", "will"],
		["no", "self"],
		["no", "cares"],
		["just", "relax"],
		["no", "worries"],
		["this", "feels", "so", "right"],
		["points", "are", "wonderful"],
		["spiral", "is", "wonderful"],
		["spiral", "is", "interesting"]
	],

	message: {
		messageDuration: 0,
		nextShowTime: 0,
		interMessageInterval: 20,
		currentMessage: 0,
		offset: 0,
		done: true,

		messages: [],

		createBookEnds: function() {
			//add random text to the beginning and end of the message array
			//this will help obfuscate the message to people trying to pay
			//close attention
			if (this.messages.length <= 0) return;
			
			var start = this.randomWord(this.messages[0]);
			this.messages.unshift(start);
			
			var end = this.randomWord(this.messages[this.messages.length-1]);
			this.messages.push(end);
		},

		randomWord: function(sample) {
			var letters = "aaabcdeeefghiiiijklmnoooopqrstuvwxyz ";
			var len = sample.length;
			var result = "";
			for (var i = 0; i < len; i++) {
				result += letters[Math.floor(Math.random()*letters.length)];
			}
			return result;
		},

		draw: function(x, y) {
			if (this.done) return;
			var now = Date.now();
			if (now >= this.nextShowTime) {
				ctx.font="20px Georgia";
				ctx.strokeStyle = "#FFFFFF";
				ctx.fillStyle = "#FFFFFF";
				ctx.fillText(this.messages[this.currentMessage],x-this.offset,y+5);
				//console.log("spiral["+this.currentMessage+"]: " + this.messages[this.currentMessage]);
				if (now >= this.nextShowTime + this.messageDuration) {
					this.nextShowTime = now + this.interMessageInterval * Math.floor(Math.random()*5);
					this.currentMessage += 1;
					if (this.currentMessage >= this.messages.length) this.done = true;
					ctx.font="20px Georgia";
					this.offset = ctx.measureText(this.messages[this.currentMessage]).width / 2;
					//console.log("next message = "+ this.currentMessage + " offset=" + this.offset);
				}
				//console.log("drawing message");
			} 
			
		}
	},

	draw: function(x, y) {
		var now = Date.now();
		if (now >= this.nextShowTime) {
			this.message.draw(x,y);

			if (this.message.done) {
				this.message.done = false;
				this.currentMessage++;
				if (this.currentMessage >= this.messages.length) this.currentMessage = 0;
				this.message.messages = [];
				for (var i = 0; i < this.messages[this.currentMessage].length; i++) {
					this.message.messages.push(this.messages[this.currentMessage][i]);
				}
				//this.message.messages = this.messages[this.currentMessage];
				this.message.createBookEnds();
				this.message.nextShowTime = now + this.interMessageInterval;
				this.nextShowTime = this.message.nextShowTime;
				this.message.currentMessage = 0;
			}
		}
	}
}

var progressController = {
}

var identity = {
	pingInterval: 5000,
	nextPingTime: 0,

	generateFingerprint: function() {
		this.resolution = screen.width + "x" + 
			screen.height;
		this.userAgent = navigator.userAgent;
		this.appName = navigator.appName;
		this.colorDepth = window.screen.colorDepth;
		this.fontSmoothing = window.screen.fontSmoothingEnabled;
		this.maxWidth = window.screen.availWidth;
		this.maxHeight = window.screen.availHeight;
		this.java = navigator.javaEnabled();
		
		this.str = "";
		var p = {};

		for (var key in this) {
			var value = this[key];
			if (this.isFunction(value)) continue;
			if (key == "str") continue;
			p[key] = value;

		}
		this.str = Base64.encode(JSON.stringify(p));

		console.log("identity: " + this.str);
	},
	isFunction: function(functionToCheck) {
		var getType = {};
 		return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
	},
	generateCallSign: function() {
		var nato = ["alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliett",
		"kilo", "lima", "mike", "november", "oscar", "papa", "quebec", "romeo", "sierra", "tango",
		"uniform", "victor", "whiskey", "xray", "yankee", "zulu", "one", "two", "three", "four",
		"five", "six", "seven", "eight", "nine", "zero"];

		var len = 4;
		var str = "";

		for (var i = 0; i < len; i++) {
			str += nato[Math.floor(Math.random() * nato.length)];
			if (i < len - 1) str += " ";
		}
		return str;
	},
	checkID: function() {
		if (docCookies.hasItem("callSign")) {
			this.callSign = docCookies.getItem("callSign");
		} else {
			this.callSign = this.generateCallSign();
			docCookies.setItem("callSign", this.callSign, 31536e3);
			var res = docCookies.getItem("callSign");
			if (res == null) this.callSign = "COULDNOTSET";
		}
		this.generateFingerprint();
	},
	tic: function() {
		if (!this.hasOwnProperty("str")) return;
		if (Date.now() < this.nextPingTime) return;

		this.nextPingTime = Date.now() + this.pingInterval;
		
		var url = "ping.php?fp=" + this.str + "&cs=" + this.callSign;
		var get = new XMLHttpRequest();
		get.onreadystatechange = null;
		get.open("GET", url, true);
		get.send(null);
		//console.log("pinging server");
	}

}

var gameController = {
	startTime: 0,
	paused: false,

	onBlur: function() {
		console.log("onBlur");
		this.pauseGame();	
	},
	onFocus: function() {
		console.log("onFocus");
		this.continueGame();
	},
	handleVisibilityChange: function() {
		console.log("vis change!");
		if (document.hidden) this.onBlur();
		else this.onFocus();
	},
	pauseGame: function() {
		console.log("paused");
		this.paused = true;
		target.mouseInsideSamples.push(0);
		scController.pause();
	},
	continueGame: function() {
		console.log("unpaused");
		if (this.paused == true) target.tic(0);
		this.paused = false;
		//target.updated = true;
		window.requestAnimationFrame(mainloop);
	}
}

function mainloop() {
	if (gameController.paused) {
		//console.log("mainloop: paused");
		return;
	}
	window.requestAnimationFrame(mainloop);
	var delta = fpswatcher.tic();

	//if (gameController.paused) return;
	//scController.tic();
	target.tic(delta);
	ctx.fillStyle = "#FFFFFF";
		
	target.draw();
	
	srEngine.draw(delta);

	identity.tic();

	//return;
	if (Date.now() >= target.lastPerformanceCheckTime + target.performanceCheckInterval) 
		console.log(delta + " " +
			target.mouseInsideSamples.length + " " + 
			target.getPerformance() + " " + 
			Date.now() + " radius=" + target.radius + " " +
			"speed(" + target.speed + ") " +
			target.x + "," + target.y + " " +
			"pos=" + scController.position);
	
	
}

function main() {
	c = document.getElementById("maincanvas");
	c.height = window.innerHeight/2-padding;
	c.width = window.innerWidth-padding;
	ctx = c.getContext("2d");
	//window.onmousemove = mouse.handleMouseMove;
	
	//Soundcloud Variables
	iframeElement   = document.querySelector('iframe');
	iframeElement	= document.getElementById("scwidget");
	iframeElementID = iframeElement.id;
	widget1         = SC.Widget(iframeElement);
	widget2         = SC.Widget(iframeElementID);

	widget1.bind(SC.Widget.Events.READY, function() {
		widget1.bind(SC.Widget.Events.PLAY, function() {
        		// get information about currently playing sound
			widget1.getCurrentSound(function(currentSound) {
				if (currentSound.hasOwnProperty("get"))
	         			console.log('sound ' + currentSound.get('') + 'began to play');
				if (currentSound.hasOwnProperty("currentPosition")) 
					console.log("current position: " + currentSound.currentPosition);
	        	});
		});
		//widget1.bind(SC.Widget.Events.PLAY_PROGRESS, scController.receivedPosition);
		widget1.bind(SC.Widget.Events.PLAY_PROGRESS, scController.receivedPosition.bind(scController));
		widget1.bind(SC.Widget.Events.FINISH, scController.loop.bind(scController));
      		//widget1.addEventListener(SC.Widget.Events.PLAY_PROGRESS, scController.receivedPosition.bind(scController),true);
	
		
		// get current level of volume
      		widget1.getVolume(function(volume) {
        		console.log('current volume value is ' + volume);
      		});
		widget1.getPosition(function(pos) {
			console.log("current position is " + pos);
		});
		//window.addEventListener(SC.Widget.Events.PLAY_PROGRESS, scController.receivedPosition.bind(scController));

      // set new volume level
      		widget1.setVolume(50);
      // get the value of the current position
    	});


	window.addEventListener("focus", gameController.onFocus.bind(gameController), true);
	window.addEventListener("blur", gameController.onBlur.bind(gameController), true);
	//document.addEventListener("visibilitychange", gameController.handleVisibilityChange.bind(gameController), true);
	//window.addEventListener('blur', function(e){console.log("blur");document.title="blurred";});
	//window.addEventListener('focus', function(e){console.log("focus");document.title="here";});
	window.addEventListener('mousemove', mouse.handleMouseMove.bind(mouse),true);
	//window.addEventListener(SC.Widget.Events.PLAY_PROGRESS, scController.receivedPosition.bind(scController), false);
	//scController.init();

	identity.checkID();

	mainloop();
}
